/*
 * Copyright (C) 2007-2008  Hylke van der Schaaf
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, in version 3 of the License.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package hextd.core;

import hextd.message.MessageFetch;
import hextd.message.MessageMap;
import hextd.message.MessageMapList;
import hextd.network.NetworkConnector;
import hextd.util.MapIO;
import hextd.util.MapListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * MapLoaderClient loads maps from the gameserver.
 * TODO: if the gameserver is not available, it loads the backup-maps from the jar.
 * @author hylke
 */
public class MapLoaderClient extends MapLoader {

	private NetworkConnector serverConnection;
	private ArrayList<MapListener> mapListeners = new ArrayList<MapListener>();

	public MapLoaderClient(NetworkConnector serverConnection) {
		this.serverConnection = serverConnection;
	}

	/**
	 * Loads the index of local map files.
	 * @param fileUrl the url of the index file for the local maps
	 * @param contextUrl the context to which the index url is relative.
	 */
	public void loadlocalIndex(String fileUrl, URL contextUrl) {
		URL indexUrl;
		ArrayList<String> levelNames;
		try {
			indexUrl = new URL(contextUrl, fileUrl);
			localMapContextUrl = indexUrl;
			levelNames = MapIO.getLevelIndex(indexUrl);
			for (String levelName : levelNames) {
				tryLoadLocalFile(levelName, indexUrl);
			}
		} catch (MalformedURLException ex) {
			Logger.getLogger(MapLoaderClient.class.getName()).log(Level.SEVERE, null, ex);
		}

		fireMapListChanged(mapListLocal);
	}

	/**
	 * Create a mapobject for a custom map.
	 * @param mapContent the map definition.
	 */
	public MapInfo parseCustomMap(String mapContent) {
		MapInfo mapInfo = mapCache.get(0);
		if (mapInfo == null) {
			mapInfo = new MapInfo();
			mapInfo.mapId = 0;
			mapListLocal.add(mapInfo);
			mapCache.put(mapInfo.mapId, mapInfo);
		}

		mapInfo.setContent(mapContent);
		// getMapData will parse the content of the map and fill the mapInfo
		GameMap mapData = mapInfo.getMapData();

		if (mapInfo.players == 0) {
			mapInfo.players = 1;
		}
		return mapInfo;
	}

	/**
	 * Parses a map-index send by the gameserver.
	 * @param mml the message containing the maplist
	 */
	public void parseRemoteIndex(MessageMapList mml) {
		mapList = mml.getMapList();
		categories = mml.getCategories();
		Map<Integer, MapInfo> maps = mml.getMapCache();
		for (Entry<Integer, MapInfo> entry : maps.entrySet()) {
			Integer id = entry.getKey();
			MapInfo cached = mapCache.get(id);
			MapInfo info = entry.getValue();
			if (cached != null && cached.getContent() != null && cached.lastChange == info.lastChange) {
				info.setContent(cached.getContent());
			}
		}
		mapCache = maps;
		System.out.println("ClientMapLoader:parseRemoteIndex: Got " + maps.size() + " maps from server.");
		fireMapListChanged(mapList);
	}

	/**
	 * Parses a map send by the gameserver.
	 * @param mm the message containing the map.
	 */
	public void parseRemoteMap(MessageMap mm) {
		MapInfo info = mapCache.get(mm.getMapId());
		if (info != null) {
			System.out.println("ClientMapLoader::parseMap: Received map (" + mm.getMapId() + ") from server.");
			info.setContent(mm.getMap());
			fireMapChanged(info);
		} else {
			System.out.println("ClientMapLoader::parseMap: Received map (" + mm.getMapId() + ") that is not in the mapCache.");
		}
	}

	@Override
	public MapInfo getMap(int mapId) {
		MapInfo info = mapCache.get(mapId);
		if (info != null && info.getContent() == null) {
			if (info.fileName == null) {
				serverConnection.sendLine(MessageFetch.formatMessage(mapId, MessageFetch.FetchType.map));
			} else {
				try {
					//System.out.println("HexTD::FetchFromUrl::file = " + fetchUrl.toString());
					BufferedReader bufferedreader;
					URL fetchUrl = new URL(localMapContextUrl, info.fileName);
					URLConnection urlconnection = fetchUrl.openConnection();
					if (urlconnection.getContentEncoding() != null) {
						bufferedreader = new BufferedReader(new InputStreamReader(urlconnection.getInputStream(), urlconnection.getContentEncoding()));
					} else {
						bufferedreader = new BufferedReader(new InputStreamReader(urlconnection.getInputStream(), "utf-8"));
					}
					String line;
					StringBuilder mapContent = new StringBuilder();
					while ((line = bufferedreader.readLine()) != null) {
						mapContent.append(line);
						mapContent.append("\n");
					}
					info.setContent(mapContent.toString());
					fireMapChanged(info);
				} catch (IOException _ex) {
					System.err.println("HexTD::readFile:: Can't read from " + info.fileName);
				}

			}
		}
		return info;
	}

	public String getCatName(Integer catId) {
		if (categories != null) {
			String name = categories.get(catId);
			if (name != null) {
				return name;
			}
		}
		return "Category " + catId;
	}

	private void fireMapChanged(MapInfo info) {
		for (MapListener l : mapListeners) {
			l.mapLoaded(info);
		}
	}

	private void fireMapListChanged(List<MapInfo> list) {
		for (MapListener l : mapListeners) {
			l.mapIndexLoaded(list);
		}
	}

	public void addMapListener(MapListener l) {
		this.mapListeners.add(l);
	}

	public void removeMapListener(MapListener l) {
		mapListeners.remove(l);
	}
}
